  package top.superflow.context.imp

import org.apache.commons.lang3.builder.ToStringBuilder
import org.scalatest.concurrent.{Eventually, PatienceConfiguration}
import org.scalatest.time.Span.{convertDurationToSpan, convertSpanToDuration}
import org.scalatest.time.SpanSugar
import org.springframework.stereotype.Component
import top.superflow.constant.GlobalConst
import top.superflow.context.api.{Context, ContextConfig}
import top.superflow.db.api._
import top.superflow.db.orientdb.{OrientDBConfiguration, OrientDBServer, OrientServerConfiguration}
import top.superflow.exception.{RequestUnsupportedException, ResponseUnsupportedException, WaitTimeoutException}
import top.superflow.log.Loggable
import top.superflow.runtime.api.Status
import top.superflow.utils.SuperflowENVUtil

import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.util.{Failure, Success}

@Component
class DBContext extends Context
                with ContextConfig
                with Eventually
                with PatienceConfiguration
                with SpanSugar
                with Loggable{
  
  val superflowDB =  OrientDBServer.getInstance
  var startResult: Future[Future[DBServer]] = _
  var shutdownResult : Future[DBServer] = _
  var loadSuperflowDBResult : Future[DBPool] = _
  var dbPool : DBPool = _
  
  
  
  val stabilizationEpoch = 30.seconds
  val stabilizationTimeout = PatienceConfiguration.Timeout(stabilizationEpoch * 2)
  val stabilizationInterval = PatienceConfiguration.Interval(1.seconds)
 
  import scala.concurrent.ExecutionContext.Implicits.global

  def init() = {
    val dbServerCfg : DBServerConfiguration = new OrientServerConfiguration(getConfig)
    this.startResult = superflowDB.startup(dbServerCfg)

    val futureDBServer = Await.result(this.startResult, Duration.Inf)
    val dbServer = Await.result(futureDBServer, Duration.Inf)
    val dbCfg : DBConfiguration = loadDBConf

    handleSuccessEvent(dbCfg, dbServer)
  }

  private def handleSuccessEvent(dbCfg: DBConfiguration, dbServer: DBServer) = {

    this.loadSuperflowDBResult = dbServer.load(dbCfg)

    this.loadSuperflowDBResult onComplete {
      case Success(dbPool) => {
        this.dbPool = dbPool
        logger.info(">>>>>>>>>>>>>>>>>>>>>Successfully star inner DB [{}]", ToStringBuilder.reflectionToString(dbCfg))
      }
      case Failure(exception) => {
        throw new ResponseUnsupportedException(exception)
      }
    }

  }

  private def loadDBConf : OrientDBConfiguration = {
    val dbHome = SuperflowENVUtil.getAssuredProperty(GlobalConst.SYSTEM_ENV_VAR_DB_HOME)
    val url = "plocal:" + dbHome
    val dbName = SuperflowENVUtil.getAssuredProperty(GlobalConst.SYSTEM_DB_CONFIG_DEFAULTDB_NAME)
    val username = SuperflowENVUtil.getAssuredProperty(GlobalConst.SYSTEM_DB_CONFIG_DEFAULTDB_ROOT_USER_NAME)
    val password = SuperflowENVUtil.getAssuredProperty(GlobalConst.SYSTEM_DB_CONFIG_DEFAULTDB_ROOT_USER_PASSWORD)
    new OrientDBConfiguration (url,dbName, username, password)
  }
  
  def waitUntil(status : Status) : Boolean = {
    status match {
        case x : DBStatus =>{
          eventually(stabilizationTimeout, stabilizationInterval) {
            if(this.superflowDB.getStatus == x){
            	return true
            }
          }
          throw new WaitTimeoutException("Wait time out for " + stabilizationEpoch * 2)
        }
        case _ => {
          throw new RequestUnsupportedException(status)
        }
      }
    true
  }
  
  def getPool : DBPool = {
    this.dbPool
  }
  
  def shutdown : Future[DBServer] = {
    this.shutdownResult = this.superflowDB.shutdown
    this.shutdownResult
  }
  
  def getCustomHome : String = {
    SuperflowENVUtil.getProperty(GlobalConst.SYSTEM_ENV_VAR_DB_HOME)
  }
  
  def getConfigFileName : String = {
    SuperflowENVUtil.getProperty(GlobalConst.SYSTEM_DB_RUNNING_CONFIG_FILE_NAME)
  }
  
  def getDefaultConfig : String = {
    SuperflowENVUtil.getProperty(GlobalConst.SYSTEM_DB_RUNNING_DEFAULT_CONFIG)
  }
}